import Details from '@/components/Details';
import Card from '@/components/Card';
import Cards from '@/components/Cards';
import Callout from '@/components/Callout';
import {CourseVideoBanner} from '@/components/CourseBanner';

# Routing configuration

## `defineRouting`

The routing configuration that is shared between the [middleware](/docs/routing/middleware) and the [navigation APIs](/docs/routing/navigation) can be defined with the `defineRouting` function.

```tsx filename="src/i18n/routing.ts"
import {defineRouting} from 'next-intl/routing';

export const routing = defineRouting({
  // A list of all locales that are supported
  locales: ['en', 'de'],

  // Used when no locale matches
  defaultLocale: 'en'
});
```

Depending on your routing needs, you may wish to consider further settings—see below.

<Details id="locales-unknown">
<summary>What if the locales aren't known at build time?</summary>

In case you're building an app where locales can be added and removed at runtime, you can provide the routing configuration for the middleware [dynamically per request](/docs/routing/middleware#composing-other-middlewares).

To create the corresponding navigation APIs, you can [omit the `locales` argument](/docs/routing/navigation#locales-unknown) from `createNavigation` in this case.

Still, in case you're defining other routing config, make sure to keep them in sync between the middleware and the navigation APIs.

</Details>

### `localePrefix`

By default, the pathnames of your app will be available under a prefix that matches your directory structure (e.g. `/en/about` → `app/[locale]/about/page.tsx`). You can however adapt the routing to optionally remove the prefix or customize it per locale by configuring the `localePrefix` setting.

**Learn more:**

<CourseVideoBanner
  className="mt-2"
  href="https://learn.next-intl.dev/chapters/06-routing/07-prefix-based"
  title="Prefix-based routing"
/>

#### `localePrefix: 'always'` (default) [#locale-prefix-always]

By default, pathnames always start with the locale (e.g. `/en/about`).

```tsx filename="routing.ts" {5}
import {defineRouting} from 'next-intl/routing';

export const routing = defineRouting({
  // ...
  localePrefix: 'always'
});
```

#### `localePrefix: 'as-needed'` [#locale-prefix-as-needed]

If you want to use no prefix for the default locale (e.g. `/about`) while keeping it for other locales (e.g. `/de/about`), you can configure your routing accordingly:

```tsx filename="routing.ts" {5}
import {defineRouting} from 'next-intl/routing';

export const routing = defineRouting({
  // ...
  localePrefix: 'as-needed'
});
```

**Note that:**

1. If you use this routing strategy, make sure that your [`matcher`](/docs/routing/middleware#matcher-config) detects unprefixed pathnames.
2. The middleware will by default set a [cookie](#locale-cookie) to remember the user's locale preference. If no explicit locale prefix is present in the pathname, then [locale detection](/docs/routing/middleware#locale-detection-prefix) will potentially redirect users to the latest locale that was matched based on the cookie value (e.g. `/` → `/de`).
3. If a superfluous locale prefix like `/en/about` is requested, the middleware will automatically redirect to the unprefixed version `/about`. This can be helpful in case you're redirecting from another locale and you want to update a potential cookie value first (e.g. [`<Link />`](/docs/routing/navigation#link) relies on this mechanism).

#### `localePrefix: 'never'` [#locale-prefix-never]

If you'd like to provide a locale to `next-intl`, e.g. based on user settings, you can consider not using locale-based routing in the first place.

However, you can also configure the middleware to never show a locale prefix in the URL, which can be helpful in the following cases:

1. You want to use [domain-based routing](#domains) and there's only one locale per domain
2. You want to use a cookie to determine the locale while enabling static rendering

```tsx filename="routing.ts" {5}
import {defineRouting} from 'next-intl/routing';

export const routing = defineRouting({
  // ...
  localePrefix: 'never'
});
```

In this case, requests for all locales will be rewritten to have the locale only prefixed internally. You still need to place all your pages inside a `[locale]` folder for the routes to be able to receive the `locale` param.

**Note that:**

1. If you use this routing strategy, make sure that your [`matcher`](/docs/routing/middleware#matcher-config) detects unprefixed pathnames.
2. [Alternate links](#alternate-links) are disabled in this mode since URLs might not be unique per locale. Due to this, consider including these yourself, or set up a [sitemap](/docs/environments/actions-metadata-route-handlers#sitemap) that links localized pages via `alternates`.
3. You can consider increasing the [`maxAge`](#locale-cookie) attribute of the locale cookie to a longer duration to remember the user's preference across sessions.

#### `prefixes` [#locale-prefix-prefixes]

If you'd like to customize the user-facing prefix, you can provide a locale-based mapping:

```tsx filename="routing.ts"
import {defineRouting} from 'next-intl/routing';

export const routing = defineRouting({
  locales: ['en-US', 'de-AT', 'zh'],
  defaultLocale: 'en-US',
  localePrefix: {
    mode: 'always',
    prefixes: {
      'en-US': '/us',
      'de-AT': '/eu/at'
      // (/zh will be used as-is)
    }
  }
});
```

**Note that:**

1. You should adapt your [`matcher`](/docs/routing/middleware#matcher-config) to match the custom prefixes.
2. Custom prefixes are only visible to the user and rewritten internally to the corresponding locale. Therefore, the `[locale]` segment corresponds to the locale, not the prefix.

<Details id="locale-prefix-custom-read-prefix">
<summary>Can I read the matched prefix in my app?</summary>

Since the custom prefix is rewritten to the locale internally, you can't access the prefix directly. However, you can extract details like the region from the locale:

```tsx
import {useLocale} from 'next-intl';

function Component() {
  // Assuming the locale is 'en-US'
  const locale = useLocale();

  // Extracts the "US" region
  const {region} = new Intl.Locale(locale);
}
```

The region must be a valid [ISO 3166-1 alpha-2 country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements) or a [UN M49 region code](https://en.wikipedia.org/wiki/UN_M49#Code_lists). When passed to `Intl.Locale`, the region code is treated as case-insensitive and normalized to uppercase. You can also combine languages with regions where the language is not natively spoken (e.g. `en-AT` describes English as used in Austria).

Apart from the region, a locale can [encode further properties](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale#description), like the numbering system.

If you'd like to encode custom information in the locale, you can use arbitrary [private extensions](https://tc39.es/proposal-intl-locale/#sec-insert-unicode-extension-and-canonicalize), denoted by the `-x-` prefix (e.g. `en-US-x-usd`). The `Intl.Locale` constructor ignores private extensions, but you can extract them from the locale string manually.

</Details>

### `pathnames` [#pathnames]

Prefer to watch a video?

<CourseVideoBanner
  className="mt-2"
  href="https://learn.next-intl.dev/chapters/06-routing/08-localized-pathnames"
  title="Localized pathnames"
/>

Many apps choose to localize pathnames, especially when search engine optimization is relevant.

**Example:**

- `/en/about`
- `/de/über-uns`

Since you typically want to define these routes only once internally, you can use the `next-intl` middleware to [rewrite](https://nextjs.org/docs/api-reference/next.config.js/rewrites) such incoming requests to shared pathnames.

```tsx filename="routing.ts"
import {defineRouting} from 'next-intl/routing';

export const routing = defineRouting({
  locales: ['en-US', 'en-UK', 'de'],
  defaultLocale: 'en-US',

  // The `pathnames` object holds pairs of internal and
  // external paths. Based on the locale, the external
  // paths are rewritten to the shared, internal ones.
  pathnames: {
    // If all locales use the same pathname, a single
    // external path can be used for all locales
    '/': '/',
    '/blog': '/blog',

    // If locales use different paths, you can
    // specify the relevant external pathnames
    '/services': {
      de: '/leistungen'
    },

    // Encoding of non-ASCII characters is handled
    // automatically where relevant
    '/about': {
      de: '/über-uns'
    },

    // Dynamic params are supported via square brackets
    '/news/[articleSlug]': {
      de: '/neuigkeiten/[articleSlug]'
    },

    // Static pathnames that overlap with dynamic segments
    // will be prioritized over the dynamic segment
    '/news/just-in': {
      de: '/neuigkeiten/aktuell'
    },

    // Also (optional) catch-all segments are supported
    '/categories/[...slug]': {
      de: '/kategorien/[...slug]'
    }
  }
});
```

Localized pathnames map to a single internal pathname that is created via the file-system based routing in Next.js. In the example above, `/de/über-uns` will be handled by the page at `/[locale]/about/page.tsx`.

<Details id="localized-pathnames-dynamic-segments">
<summary>How can I localize CMS-driven URLs?</summary>

If you have a route like `/news/[articleSlug]`, you may want to localize your pathnames like this:

```
/en/news/launch-of-new-product
/de/neuigkeiten/produktneuheit
```

In this case, you can use `pathnames` to localize the static part of the pathname:

```tsx
'/news/[articleSlug]': {
  de: '/neuigkeiten/[articleSlug]'
}
```

… and use a localized slug from your CMS for the dynamic part.

When doing this, make sure your locale switcher and alternate links are also aware of your CMS-driven URLs so they point to the correct localized pathnames.

**Learn more:**

<CourseVideoBanner
  className="mt-2"
  href="https://learn.next-intl.dev/chapters/07-content/03-cms-driven-urls"
  title="CMS-driven URLs"
/>

</Details>

<Details id="localized-pathnames-revalidation">
<summary>How can I revalidate localized pathnames?</summary>

Depending on if a route is generated statically (at build time) or dynamically (at runtime), [`revalidatePath`](https://nextjs.org/docs/app/api-reference/functions/revalidatePath) needs to be called either for the localized or the internal pathname.

Consider this example:

```
app
└── [locale]
    └── news
        └── [slug]
```

… with this routing configuration:

```tsx filename="routing.ts"
import {defineRouting} from 'next-intl/routing';

export const routing = defineRouting({
  locales: ['en', 'de'],
  defaultLocale: 'en',
  pathnames: {
    '/news/[slug]': {
      en: '/news/[slug]',
      de: '/neuigkeiten/[slug]'
    }
  }
});
```

Depending on whether `some-article` was included in [`generateStaticParams`](https://nextjs.org/docs/app/api-reference/functions/generate-static-params) or not, you can revalidate the route like this:

```tsx
// Statically generated at build time
revalidatePath('/de/news/some-article');

// Dynamically generated at runtime:
revalidatePath('/de/neuigkeiten/some-article');
```

When in doubt, you can revalidate with `revalidateTag` instead.

See also: [`vercel/next.js#59825`](https://github.com/vercel/next.js/issues/59825)

</Details>

### `domains`

Prefer to watch a video?

<CourseVideoBanner
  className="mt-2"
  href="https://learn.next-intl.dev/chapters/06-routing/06-domain-based"
  title="Domain-based routing"
/>

If you want to serve your localized content based on different domains, you can provide a list of mappings between domains and locales via the `domains` setting.

**Examples:**

- `us.example.com`: `en-US`
- `ca.example.com`: `en-CA`
- `ca.example.com/fr`: `fr-CA`
- `fr.example.com`: `fr-FR`

In many cases, `domains` are combined with a [`localePrefix`](#locale-prefix) setting to achieve results as shown above. Also [custom prefixes](#locale-prefix-prefixes) can be used to customize the user-facing prefix per locale.

```tsx filename="routing.ts"
import {defineRouting} from 'next-intl/routing';

export const routing = defineRouting({
  locales: ['en-US', 'en-CA', 'fr-CA', 'fr-FR'],
  defaultLocale: 'en-US',
  domains: [
    {
      domain: 'us.example.com',
      defaultLocale: 'en-US',
      locales: ['en-US']
    },
    {
      domain: 'ca.example.com',
      defaultLocale: 'en-CA',
      locales: ['en-CA', 'fr-CA']
    },
    {
      domain: 'fr.example.com',
      defaultLocale: 'fr-FR',
      locales: ['fr-FR']
    }
  ],
  localePrefix: {
    mode: 'as-needed',
    prefixes: {
      // Cleaner prefix for `ca.example.com/fr`
      'fr-CA': '/fr'
    }
  }
});
```

Locales are required to be unique across domains, therefore regional variants are typically used to avoid conflicts. Note however that you don't necessarily need to [provide messages](/docs/usage/configuration#messages) for each locale if the overall language is sufficient for your use case.

If no domain matches, the middleware will fall back to default locale matching based on [prefixes](/docs/routing/middleware#location-detection-prefix) (e.g. on `localhost`, for local development).

See also: [Domain-based routing](/docs/routing/middleware#location-detection-domain)

<Details id="domains-localeprefix-individual">
<summary>Can I use a different `localePrefix` setting per domain?</summary>

While this is currently not supported out of the box, you can still achieve this by building the app for each domain separately while injecting diverging routing configuration via an environment variable.

**Example:**

```tsx filename="routing.ts"
import {defineRouting} from 'next-intl/routing';

const isUsDomain =
  process.env.VERCEL_PROJECT_PRODUCTION_URL === 'us.example.com';

export const routing = defineRouting({
  locales: isUsDomain ? ['en-US'] : ['en-CA', 'fr-CA'],
  defaultLocale: isUsDomain ? 'en-US' : 'en-CA',
  localePrefix: isUsDomain ? 'never' : 'always'
});
```

</Details>

### `localeDetection` [#locale-detection]

The middleware will [detect a matching locale](/docs/routing/middleware#locale-detection) based on your routing configuration & the incoming request and will either pass the request through for a matching locale or redirect to one that matches.

If you want to rely entirely on the URL to resolve the locale, you can set the `localeDetection` property to `false`. This will disable locale detection based on the `accept-language` header and a potentially existing cookie value from a previous visit.

```tsx filename="routing.ts" {5}
import {defineRouting} from 'next-intl/routing';

export const routing = defineRouting({
  // ...
  localeDetection: false
});
```

In this case, only the locale prefix and a potentially [matching domain](#domains) are used to determine the locale.

### `localeCookie` [#locale-cookie]

If a user changes the locale to a value that doesn't match the `accept-language` header, `next-intl` will set a session cookie called `NEXT_LOCALE` that contains the most recently detected locale. This is used to [remember the user's locale](/docs/routing/middleware#locale-detection) preference for subsequent requests.

By default, the cookie will be configured with the following attributes:

2. [**`sameSite`**](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value): This value is set to `lax` so that the cookie can be set when coming from an external site.
3. [**`path`**](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#pathpath-value): This value is not set by default, but will use the value of your [`basePath`](#basepath) if configured.

If you have more specific requirements, you can adjust these settings accordingly:

```tsx filename="routing.ts"
import {defineRouting} from 'next-intl/routing';

export const routing = defineRouting({
  // ...

  // Will be merged with the defaults
  localeCookie: {
    // Custom cookie name
    name: 'USER_LOCALE',
    // Expire in one year
    maxAge: 60 * 60 * 24 * 365
  }
});
```

… or turn the cookie off entirely:

```tsx filename="routing.ts"
import {defineRouting} from 'next-intl/routing';

export const routing = defineRouting({
  // ...

  localeCookie: false
});
```

<Details id="locale-cookie-gdpr">
<summary>Which `maxAge` value should I consider for GDPR compliance?</summary>

To be compliant out of the box, `next-intl` does not set the `max-age` value of the cookie, making it a session cookie that expires when a browser is closed.

Please note that legal requirements may vary by region, so it's advisable to verify them independently. While we strive to keep this information as up-to-date as possible, we cannot guarantee its accuracy.

**Learn more:**

<CourseVideoBanner
  className="mt-2"
  href="https://learn.next-intl.dev/chapters/06-routing/09-cookie-regulations"
  title="Cookie regulations"
/>

</Details>

### `alternateLinks` (`hreflang`) [#alternate-links]

The middleware automatically sets the [`link`](https://developers.google.com/search/docs/specialty/international/localized-versions#http) header to inform search engines that your content is available in different languages. Note that this automatically integrates with your routing strategy and will generate the correct links based on your configuration.

However, there are cases where you may want to provide these links yourself:

1. You have pages that are only available for certain locales
2. You're using an external system like a CMS to manage pathnames of your pages

In this case, you can opt-out of this behavior by setting `alternateLinks` to `false`:

```tsx filename="routing.ts"
import {defineRouting} from 'next-intl/routing';

export const routing = defineRouting({
  // ...

  alternateLinks: false
});
```

**Learn more:**

<CourseVideoBanner
  className="mt-2"
  href="https://learn.next-intl.dev/chapters/08-seo/03-alternate-links"
  title="hreflang & canonicals"
/>

<CourseVideoBanner
  className="mt-2"
  href="https://learn.next-intl.dev/chapters/08-seo/04-sitemap"
  title="Sitemaps"
/>

<Details id="alternate-links-details">
<summary>Which alternate links are included?</summary>

Using the middleware defaults, the `link` header of a response for `/` will look like this:

```
link: <https://example.com/en>; rel="alternate"; hreflang="en",
      <https://example.com/de>; rel="alternate"; hreflang="de",
      <https://example.com/>; rel="alternate"; hreflang="x-default"
```

The [`x-default`](https://developers.google.com/search/docs/specialty/international/localized-versions#xdefault) entry is included to point to a variant that can be used if no other language matches the user's browser setting. This special entry is reserved for language selection & detection, in our case issuing a 307 redirect to the best matching locale.

To provide fully specified URLs, the domain is read from the `x-forwarded-host` header, with a fallback to `host` (hosting platforms typically provide these headers out of the box).

Your middleware configuration, including options like `domains`, `pathnames` and `basePath`, is automatically incorporated.

</Details>

<Details id="alternate-links-customization">
<summary>Can I customize the alternate links?</summary>

If you need to customize the alternate links, you can either turn them off and provide your own implementation, or if you only need to make minor adaptions, you can [compose the middleware](#composing-other-middlewares) and add your custom logic after the middleware has run:

```tsx filename="middleware.ts"
import createMiddleware from 'next-intl/middleware';
import LinkHeader from 'http-link-header';
import {NextRequest} from 'next/server';
import {routing} from './i18n/routing';

const handleI18nRouting = createMiddleware(routing);

export default async function middleware(request: NextRequest) {
  const response = handleI18nRouting(request);

  // Example: Remove the `x-default` entry
  const link = LinkHeader.parse(response.headers.get('link'));
  link.refs = link.refs.filter((entry) => entry.hreflang !== 'x-default');
  response.headers.set('link', link.toString());

  return response;
}
```

</Details>

## `next.config.ts` [#next-config]

Apart from your routing configuration, `next-intl` will also incorporate settings from [`next.config.ts`](https://nextjs.org/docs/pages/api-reference/config/next-config-js).

### `basePath`

The middleware as well as the navigation APIs will automatically consider a [`basePath`](https://nextjs.org/docs/app/api-reference/next-config-js/basePath) that you might have configured in your Next.js config.

The only exception is the [`getPathname`](/docs/routing/navigation#getpathname) function, which will return the bare pathname without a base path. Due to this, you can prefix the returned pathname manually if you need to.

When using a base path, you should make sure that your [`matcher`](#matcher-config) handles an explicit root:

```tsx filename="proxy.ts"
export const config = {
  // The `matcher` is relative to the `basePath`
  matcher: [
    // This entry handles the root of the base
    // path and should always be included
    '/'

    // ... other matcher config
  ]
};
```

### `trailingSlash`

If you have [`trailingSlash`](https://nextjs.org/docs/app/api-reference/next-config-js/trailingSlash) set to `true` in your Next.js config, this setting will be taken into account by the middleware and the navigation APIs.

Note that if you're using [`pathnames`](#pathnames), your internal and external pathnames can be defined either with or without a trailing slash as they will be normalized internally.
